﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using System;
using Kylin;

public class Publisher {

    private static readonly string originalHotFixPath = ResourceHelper.originalHotFixPath;
    private static readonly string targetHotFixMovePath = ResourceHelper.targetHotFixMovePath;
    private static readonly string bundleConfigPath = ResourceHelper.bundleConfigPath;
    private static readonly string luaFolderName = ResourceHelper.luaFolderName;
    private static readonly string win64Platform = ResourceHelper.win64Platform;
    private static readonly string osxPlatform = ResourceHelper.osxPlatform;
    private static readonly string fullListFileName = "fulllist.txt";
    private static readonly string hotfixFileName = "hotfixlist.txt";
    private static readonly string dFileName = "d.txt";
    private static readonly string iOSPlatform = "iOS";
    private static readonly string androidPlatform = "Android";
    private static readonly string revisionFileName = "revision.txt";
    private static readonly string hotfixTemporaryFolderName = "HotFix_Temporary";
    private static readonly string fullpackageTemporaryFolderName = "Fullpackage_Temporary";

    private static string currentPlatform = "ErrorPlatform";
    private static string currentRevisionFolder = string.Empty;
    private static int currentRevision = -1;
    
    [MenuItem("Kylin/发布/iOS/发布iOS更新数据包")]
	public static void GenDataPackageiOS() {
        currentPlatform = iOSPlatform;

        //暂时切换一定的变量环境
        bool needRestoreToAssetBundle = false;
        if (ResourceLoadConfig.IsUseAssetBundle())
        {
            ResourceLoadConfig.SetUseResource();
            needRestoreToAssetBundle = true;
        }

        bool success = PreProcess();
        if (!success)
        {
            Debug.LogError("方法 PreProcess 中发生错误，请查看前面具体信息！");
            return;
        }

        success = BundleGenerate.BuildAssetBundlesiOS(currentRevisionFolder);
        if (!success)
        {
            Debug.LogError("方法 BuildAssetBundlesWin 中发生错误，应该是BuildPipleline中有什么错误了！");
            return;
        }

        PostProcess();

        //还原环境
        if (needRestoreToAssetBundle)
        {
            ResourceLoadConfig.SetUseAssetBundle();
        }
    }
	
	[MenuItem("Kylin/发布/Android/发布Android更新数据包")]
	public static void GenDataPackageAndroid() {
        currentPlatform = androidPlatform;

        //暂时切换一定的变量环境
        bool needRestoreToAssetBundle = false;
        if (ResourceLoadConfig.IsUseAssetBundle())
        {
            ResourceLoadConfig.SetUseResource();
            needRestoreToAssetBundle = true;
        }

        bool success = PreProcess();
        if (!success)
        {
            Debug.LogError("方法 PreProcess 中发生错误，请查看前面具体信息！");
            return;
        }

        success = BundleGenerate.BuildAssetBundlesAndroid(currentRevisionFolder);
        if (!success)
        {
            Debug.LogError("方法 BuildAssetBundlesWin 中发生错误，应该是BuildPipleline中有什么错误了！");
            return;
        }

        PostProcess();

        //还原环境
        if (needRestoreToAssetBundle)
        {
            ResourceLoadConfig.SetUseAssetBundle();
        }
    }
	
	[MenuItem("Kylin/发布/Win64/发布Win64更新数据包")]
	public static void GenDataPackageWin64() {
        currentPlatform = win64Platform;

        //暂时切换一定的变量环境
        bool needRestoreToAssetBundle = false;
        if (ResourceLoadConfig.IsUseAssetBundle())
        {
            ResourceLoadConfig.SetUseResource();
            needRestoreToAssetBundle = true;
        }

        bool success = PreProcess();
        if (!success)
        {
            Debug.LogError("方法 PreProcess 中发生错误，请查看前面具体信息！");
            return;
        }

        success = BundleGenerate.BuildAssetBundlesWin(currentRevisionFolder);
        if (!success)
        {
            Debug.LogError("方法 BuildAssetBundlesWin 中发生错误，应该是BuildPipleline中有什么错误了！");
            return;
        }

        PostProcess();

        //还原环境
        if (needRestoreToAssetBundle)
        {
            ResourceLoadConfig.SetUseAssetBundle();
        }
    }
	
	[MenuItem("Kylin/发布/OSX/发布OSX更新数据包")]
	public static void GenDataPackageOSX() {
        currentPlatform = osxPlatform;

        //暂时切换一定的变量环境
        bool needRestoreToAssetBundle = false;
        if (ResourceLoadConfig.IsUseAssetBundle())
        {
            ResourceLoadConfig.SetUseResource();
            needRestoreToAssetBundle = true;
        }

        bool success = PreProcess();
        if (!success)
        {
            Debug.LogError("方法 PreProcess 中发生错误，请查看前面具体信息！");
            return;
        }

        success = BundleGenerate.BuildAssetBundlesOSX(currentRevisionFolder);
        if (!success)
        {
            Debug.LogError("方法 BuildAssetBundlesWin 中发生错误，应该是BuildPipleline中有什么错误了！");
            return;
        }

        PostProcess();

        //还原环境
        if (needRestoreToAssetBundle)
        {
            ResourceLoadConfig.SetUseAssetBundle();
        }
    }
	
	[MenuItem("Kylin/发布/iOS/发布iOS更新程序包")]
	public static void GenProgramPackageiOS() {
		
	}

	[MenuItem("Kylin/发布/Android/发布Android更新程序包")]
	public static void GenProgramPackageAndroid() {

        //TODO: Remove scenes in Editor setting list and also in original HotFix folder
        DirectoryInfo di = new DirectoryInfo(originalHotFixPath + "/Scene");
        FileInfo[] fileInfos = di.GetFiles("*.unity");
        string[] sceneFilePathes = new string[fileInfos.Length];
        for (int i = 0; i < fileInfos.Length; ++i)
        {
            sceneFilePathes[i] = originalHotFixPath + "/Scene" + fileInfos[i].Name;
        }

        foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes)
        {
            //如果上面的scene在setting面板中是enabled，暂时disable它，然后记录下来，以便后面恢复现场
            if (scene.enabled)
            {
                //for ()
            }
        }

        //TODO: Remove HotFix folder from Resources
        Directory.Move(originalHotFixPath, targetHotFixMovePath);
		
		
		BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions();
        //buildPlayerOptions.scenes = new[] { "Assets/Scene1.unity", "Assets/Scene2.unity" };
        //buildPlayerOptions.locationPathName = "iOSBuild";
        buildPlayerOptions.target = BuildTarget.Android;
        // Unity says: Enabling CompressWithLz4 in Android might be a significant performance boost when loading data LZ4 decompressing is faster than the default Zip decompressing.
		buildPlayerOptions.options = BuildOptions.CompressWithLz4;
		

        //BuildReport report = BuildPipeline.BuildPlayer(buildPlayerOptions);
        //BuildSummary summary = report.summary;

        //if (summary.result == BuildResult.Succeeded)
        //{
        //    Debug.Log("Build succeeded: " + summary.totalSize + " bytes");
        //}

        //if (summary.result == BuildResult.Failed)
        //{
        //    Debug.Log("Build failed");
        //}
		
		// TODO: PUT BACK HotFix folder
		Directory.Move(originalHotFixPath, targetHotFixMovePath);
		
		// TODO: add remove scene to Editor setting		
	}

	[MenuItem("Kylin/发布/Win64/发布Win64更新程序包")]
	public static void GenProgramPackageWin64() {
		
	}
	
	[MenuItem("Kylin/发布/OSX/发布OSX更新程序包")]
	public static void GenProgramPackageOSX() {
		
	}
	
	private static void CopyLuaFiles(string baseFolder) {
        string source = Application.dataPath + "/Lua";

	}
	
	private static string[] FindEnabledEditorScenes()
    {
        List<string> EditorScenes = new List<string>();
        foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes)
        {
            if (!scene.enabled) continue;
            EditorScenes.Add(scene.path);
        }

        return EditorScenes.ToArray();
    }

    private static void RemoveDir(string dir)
    {
        if (Directory.Exists(dir))
        {
            Directory.Delete(dir, true);
        }
    }

    // generate file: each line is "relative path based on [revisonFolder], file md5"
    private static void GenerateListings(string revisionFolder)
    {
        List<string> allFilePath = FileUtils.GetAllFileInDir(revisionFolder);
        string path = revisionFolder + "/" + fullListFileName;

        StreamWriter writer = File.CreateText(path);
        for (int i = 0; i < allFilePath.Count; ++i)
        {
            if (Path.GetExtension(allFilePath[i]).Equals(".manifest")) continue;
            string md5 = FileUtils.GetFileMD5(allFilePath[i]);
            string filePathRelative = allFilePath[i].Replace(revisionFolder, "");
            filePathRelative = filePathRelative.Replace('\\', '/');
            string line = filePathRelative + "," + md5;
            writer.WriteLine(line);
        }

        writer.Flush();
        writer.Close();
        writer.Dispose();

        // set it to readonly
        File.SetAttributes(path, FileAttributes.ReadOnly);
    }

    private static void GenerateHotfixPackage(string revisionFolder, int previousRevision)
    {
        string currentRevisionListFilePath = revisionFolder + "/" + fullListFileName;
        string previousRevisionListFilePath = revisionFolder + "/../" + previousRevision + "/" + fullListFileName;

        if (!File.Exists(currentRevisionListFilePath))
        {
            Debug.Log("<color=cyan>Error: 当前数据包清单文件不存在！</color>");
            return;
        }

        if (!File.Exists(previousRevisionListFilePath))
        {
            Debug.Log("<color=cyan>Error: 前版本的数据包清单文件不存在！</color>");
            return;
        }

        ListFileProcessor currentList = new ListFileProcessor(currentRevisionListFilePath);
        ListFileProcessor previousList = new ListFileProcessor(previousRevisionListFilePath);
        currentList.Init();
        previousList.Init();

        // get the hotfix list
        Dictionary<string, string> filesCanBeDeleteDic = new Dictionary<string, string>();
        Dictionary<string, string> hotfixFileDic = currentList.Compare(previousList, ref filesCanBeDeleteDic);

        if (hotfixFileDic.Count == 0)
        {
            Debug.LogError("似乎没有修改的东西找到，新的包文件和上一个版本[" + previousRevision + "]比较完全相同！" );
            return;
        }

        // write delete list file
        string filesCanBeDeleteListFilePath = revisionFolder + "/" + dFileName;
        StreamWriter writer = File.CreateText(filesCanBeDeleteListFilePath);
        foreach (KeyValuePair<string, string> entry in filesCanBeDeleteDic)
        {
            string line = entry.Key + "," + entry.Value;
            writer.WriteLine(line);
        }
        writer.Flush();
        writer.Close();
        writer.Dispose();
        File.SetAttributes(filesCanBeDeleteListFilePath, FileAttributes.ReadOnly);

        // write hotfix list to file AND record it for future use
        List<string> hotfixFileList = new List<string>();
        string hotfixFileListPath = revisionFolder + "/" + hotfixFileName;
        writer = File.CreateText(hotfixFileListPath);
        foreach(KeyValuePair<string, string> entry in hotfixFileDic)
        {
            string line = entry.Key + "," + entry.Value;
            writer.WriteLine(line);
            hotfixFileList.Add(entry.Key);
        }
        writer.Flush();
        writer.Close();
        writer.Dispose();
        File.SetAttributes(hotfixFileListPath, FileAttributes.ReadOnly);

        //// zip hotfix package
        // copy all hotfix files to temporary folder
        string tempFolder = revisionFolder + "/" + hotfixTemporaryFolderName;
        Directory.CreateDirectory(tempFolder);
        for (int i = 0; i < hotfixFileList.Count; ++i)
        {
            string source = revisionFolder + "/" + hotfixFileList[i];
            string destination = tempFolder + "/" + hotfixFileList[i];
            FileInfo fi = new FileInfo(destination);
            fi.Directory.Create();
            File.Copy(source, destination); 
        }

        string zipFile = revisionFolder + "/hotfix.zip";
        RevisionZipUtils.ZipFile(zipFile, tempFolder);
    }

    private static void GenerateFirstRevision(string revisionFolder, int previousRevision)
    {
        string currentRevisionListFilePath = revisionFolder + "/" + fullListFileName;

        if (!File.Exists(currentRevisionListFilePath))
        {
            Debug.Log("<color=cyan>Error: 当前数据包清单文件不存在！</color>");
            return;
        }

        ListFileProcessor currentList = new ListFileProcessor(currentRevisionListFilePath);
        currentList.Init();

        string tempFolder = revisionFolder + "/" + hotfixTemporaryFolderName;
        Directory.CreateDirectory(tempFolder);
        foreach (KeyValuePair<string, string> entry in currentList.GetFileDic())
        {
            string source = revisionFolder + "/" + entry.Key;
            string destination = tempFolder + "/" + entry.Key;
            FileInfo fi = new FileInfo(destination);
            fi.Directory.Create();
            File.Copy(source, destination);
        }

        string zipFile = revisionFolder + "/hotfix.zip";
        RevisionZipUtils.ZipFile(zipFile, tempFolder);
    }

    private static string GetPublishFolder()
    {
        string productPublishFolder = PublishConfig.Instance().GetProductPublishFolder();
        string version = PublishConfig.Instance().GetVersion();

        return Application.dataPath + "/../../" + productPublishFolder + "/" + currentPlatform + "/" + version;
    }

    private static string PrepareRevison(out int revisionGenerated)
    {
        string revisionFolder;
        revisionGenerated = -1;
        try
        {
            bool firstRevision = true;
            int revision = 0;

            // locate at folder contain revision.txt
            string targetDirectory = GetPublishFolder();
            if (!Directory.Exists(targetDirectory))
            {
                Directory.CreateDirectory(targetDirectory);
            }

            // see if it's first revision
            string revisionFile = targetDirectory + "/" + revisionFileName;
            if (File.Exists(revisionFile))
            {
                firstRevision = false;
            }


            Revision.Instance().SetPath(revisionFile);
            Revision.Instance().Prepare(firstRevision);

            if (firstRevision)
            {
                revision = PublishConfig.Instance().GetRevisionBase();
            }
            else
            {
                revision = Revision.Instance().GetLatestRevision();
                revision++; //this is the new revison we target
            }

            revisionGenerated = revision;

            // need creeate the folder
            revisionFolder = targetDirectory + "/" + revision;
            Directory.CreateDirectory(revisionFolder);
        }
        catch (Exception e)
        {
            Debug.LogError("方法 PrepareRevison 中发生错误，具体信息：" + e.ToString());
            return null;
        }

        return revisionFolder;
    }

    private static void ZipFullPackage(string revisionFolder)
    {
        string currentRevisionListFilePath = revisionFolder + "/" + fullListFileName;

        if (!File.Exists(currentRevisionListFilePath))
        {
            Debug.Log("<color=cyan>Error: 当前数据包清单文件不存在！</color>");
            return;
        }

        ListFileProcessor currentList = new ListFileProcessor(currentRevisionListFilePath);
        currentList.Init();

        string tempFolder = revisionFolder + "/" + fullpackageTemporaryFolderName;
        Directory.CreateDirectory(tempFolder);
        foreach (KeyValuePair<string, string> entry in currentList.GetFileDic())
        {
            string source = revisionFolder + "/" + entry.Key;
            string destination = tempFolder + "/" + entry.Key;
            FileInfo fi = new FileInfo(destination);
            fi.Directory.Create();
            File.Copy(source, destination);
        }

        string zipFile = revisionFolder + "/fullpack.zip";
        RevisionZipUtils.ZipFile(zipFile, tempFolder);
    }

    private static bool PreProcess()
    {
        Debug.Log("<color=green>--------------------------------------------------------------------</color>");
        Debug.Log("<color=green>--------------------FitFun PackMan Start----------------------------</color>");
        Debug.Log("<color=green>--------------------------------------------------------------------</color>");


        PublishConfig.Instance().Init();
        BundleConfig.Instance().Init();

        BundleGenerate.SetCurrentPlatform(currentPlatform);
        BundleGenerate.AutoAssignPath();

        // build bundle to target revision folder
        int revision = -1;
        string revisionFolder = PrepareRevison(out revision);
        if (revisionFolder == null || revision == -1)
        {
            Debug.LogError("方法 PrepareRevison 中发生错误，退出！");
            return false;
        }

        currentRevisionFolder = revisionFolder;
        currentRevision = revision;
        return true;
    }

    private static void PostProcess()
    {
        Dictionary<int, string> ignoreFolderDic = new Dictionary<int, string>();
        ignoreFolderDic[0] = ".vscode";
        ignoreFolderDic[1] = ".idea";

        // copy lua to target revision folder
        ResourceHelper.CopyLua(Application.dataPath + "/" + luaFolderName, currentRevisionFolder + "/" + luaFolderName, ignoreFolderDic, ".txt");
        ResourceHelper.CopyGenerateBundleConfig(currentRevisionFolder);
        ResourceHelper.CopyGUIDependencyFile(currentRevisionFolder);

        // generate listing files (full list)
        GenerateListings(currentRevisionFolder);

        // zip the full package
        ZipFullPackage(currentRevisionFolder);

        if (currentRevision > Revision.Instance().GetLatestRevision())
        {
            // generate hotfix package(zip file)
            GenerateHotfixPackage(currentRevisionFolder, Revision.Instance().GetLatestRevision());
        }
        else
        {
            GenerateFirstRevision(currentRevisionFolder, Revision.Instance().GetLatestRevision());
        }

        if (currentRevision == PublishConfig.Instance().GetRevisionBase())
        {
            Revision.Instance().UpdateLatestRevionAndSave(currentRevision, true);
        }
        else
        {
            Revision.Instance().UpdateLatestRevionAndSave(currentRevision, false);
        }

        Debug.Log("<color=green>--------------------------------------------------------------------</color>");
        Debug.Log("<color=green>--------------------FitFun PackMan End------------------------------</color>");
    }
}
